#!/bin/bash -e
#
#   Neutron3529's Unity Game Plugin
#   Copyright (C) 2022 Neutron3529
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Affero General Public License as
#   published by the Free Software Foundation, either version 3 of the
#   License, or (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU Affero General Public License for more details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
############################################################################
#
#   * compile instructions: put this file and `utils.cs` in `steamapps`
#   * folder, open a terminal in the same folder, and execute:
#   *
#   * ```
#   *     chmod +x ${file}.cs
#   *     ./${file}.cs
#   * ```
#   *
#   * then the mod will be compiled automatically.
#   *
#   * Here we wrote a shebang like file, which is correct
#   * in my computer (Manjaro XFCE), if such script do not work
#   * in your computer, you could just try the instructions below :

export GAME_NAME="${0%\.cs}"                                # might modify if the name mismatch.
export GAME_DIR="$GAME_NAME"                                # might be modified, but "$GAME_NAME" cover most of the cases.

export FILE_NAME="$0"
export ASSEMBLY="Assembly-CSharp"                           # might be modified
export UTILS="utils.cs"                                     # might be modified if you do not put utils.cs in the current dir.
export PLUGIN_ID="Neutron3529.Cheat"                        # should be modified
export NAMESPACE_ID="Neutron3529.Cheat"                     # should be modified
export GAME_BASE_DIR="common/$GAME_DIR"                     # should modify GAME_DIR instead since GAME_DIR == GAME_NAME is almost always true.

export IFS=$'\n' # to disable the annoying space.
export DOTNET="dotnet" # the location of the DOTNET executable file.
[ -z "$DOTNET_CSC_DLL" ] && export DOTNET_CSC_DLL=`\ls /usr/share/dotnet/sdk/*/Roslyn/bincore/csc.dll` # In manjaro, the csc.dll is located in /usr/share/dotnet/sdk/*/Roslyn/bincore/csc.dll

__MODE_VERBOSE=99 # is the line number of "#define VERBOSE", may be modified
__MODE_DEBUG__=$((__MODE_VERBOSE+1))
__MODE_RELEASE=$((__MODE_DEBUG__+1))

case $1 in
    V)       _MODE__SELECT_=$__MODE_VERBOSE     ;;
    v)       _MODE__SELECT_=$__MODE_VERBOSE     ;;
    VERBOSE) _MODE__SELECT_=$__MODE_VERBOSE     ;;
    verbose) _MODE__SELECT_=$__MODE_VERBOSE     ;;
    D)       _MODE__SELECT_=$__MODE_DEBUG__     ;;
    d)       _MODE__SELECT_=$__MODE_DEBUG__     ;;
    DEBUG)   _MODE__SELECT_=$__MODE_DEBUG__     ;;
    debug)   _MODE__SELECT_=$__MODE_DEBUG__     ;;
    *)       _MODE__SELECT_=$__MODE_RELEASE     ;;
esac

( yes "" | head -n $_MODE__SELECT_ | head -n-1  ; tail $FILE_NAME -n+$_MODE__SELECT_ ; cat $UTILS) | sed s/%%NAMESPACE_ID%%/${NAMESPACE_ID}/g | sed s/%%PLUGIN_ID%%/${PLUGIN_ID}/g | $DOTNET $DOTNET_CSC_DLL -nologo -t:library \
  -r:"${GAME_BASE_DIR}/BepInEx/core/BepInEx.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/core/0Harmony.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/core/BepInEx.Harmony.dll" \
  `[ -e "${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/netstandard.dll" ] && echo "-r:\"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/netstandard.dll\""` \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/System.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/System.Core.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/UnityEngine.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/UnityEngine.AIModule.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/UnityEngine.CoreModule.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/UnityEngine.UI.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/mscorlib.dll" \
  $(for i in "${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/$ASSEMBLY"*.dll ; do echo -e "-r:\"$i\"\n" ; done) \
  -out:"${GAME_BASE_DIR}/BepInEx/plugins/${FILE_NAME%.*}".dll \
  -optimize `[ "$_MODE__SELECT_" == "$__MODE_DEBUG__" ] && echo -debug` \
  - && rm -f "${GAME_BASE_DIR}/BepInEx/config/${PLUGIN_ID}.cfg";

if [ -n "$2" ]; then
    git add ${FILE_NAME}
    case $2 in
        R) git commit -am "`curl -s https://whatthecommit.com/index.txt`" ;;
        r) git commit -am "`curl -s https://whatthecommit.com/index.txt`" ;;
        RANDOM) git commit -am "`curl -s https://whatthecommit.com/index.txt`" ;;
        random) git commit -am "`curl -s https://whatthecommit.com/index.txt`" ;;
        U) git commit -am "`curl -s https://whatthecommit.com/index.txt`" ;;
        u) git commit -am "`curl -s https://whatthecommit.com/index.txt`" ;;
        UPLOAD) git commit -am "`curl -s https://whatthecommit.com/index.txt`" ;;
        upload) git commit -am "`curl -s https://whatthecommit.com/index.txt`" ;;
        *) git commit -am "$2" ;;
    esac
    git push
fi
exit

#define VERBOSE // the line of __MODE_VERBOSE
#define DEBUG



using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

namespace %%NAMESPACE_ID%%
{
    [BepInPlugin("%%PLUGIN_ID%%", "%%NAMESPACE_ID%%", "0.1.0")]
    public partial class Cheat : BaseUnityPlugin {
        void Start2() {
            // 主要逻辑放在`utils.cs`中，这里的start2只是为了以防万一
            // 目前来说，这个函数的唯一用途是用来“叮”……
            // 就像这样：
            logger("叮~修改器启动，请安心游戏");
        }
        [Desc("最大能量数增乘数",10)]
        class Constant_defaultEnergy : Bint {
            public static int val;public void Init() {
                base.Init(ref val);
                if(this.Met(val)){
                    Constant.defaultEnergy*=val;
                }
            }
        }
        [Desc("退休金乘数",10)]
        class Constant_fatherRetiredSalary : Bint {
            public static int val;public void Init() {
                base.Init(ref val);
                if(this.Met(val)){
                    Constant.fatherRetiredSalary*=val;
                }
            }
        }
        [Desc("耐心乘数",10)]
        class Constant_defaultPatient : Bint {
            public static int val;public void Init() {
                base.Init(ref val);
                if(this.Met(val)){
                    Constant.defaultPatient*=val;
                }
            }
        }
        [Desc("课程分数乘数",10)]
        class Constant_eachAddScore : Bint {
            public static int val;public void Init() {
                base.Init(ref val);
                if(this.Met(val)){
                    Constant.eachAddScore*=val;
                }
            }
        }
        [Desc("工作速度乘数",0.1)]
        [HarmonyPatch(typeof(MapSys), "DoingSthEnu")]
        class Constant_workingAnimTime : Bfloat {
            public static float val;public void Init() {
                base.Init(ref val);
                if(this.Met(val)){
                    typeof(MapSys).GetField("workingAnimTime",(BindingFlags)(-1)).SetValue(null,val*(float)(typeof(MapSys).GetField("workingAnimTime",(BindingFlags)(-1)).GetValue(null)));
                }
            }
            static IEnumerable<MethodBase> TargetMethods(){
                foreach(MethodInfo i in new MethodInfo[]{
                    typeof(MapSys).GetMethod("DoingSthEnu",(BindingFlags)(-1)),
                    typeof(ChatSys).GetMethod("StartChallengeEnu",(BindingFlags)(-1))
                }){
                    if(i==null){logger("FATAL:NULL i");}else{
                        logger("修改类别"+i.ToString()+"的MoveNext方法");
                        if(AccessTools.EnumeratorMoveNext(i)!=null){yield return AccessTools.EnumeratorMoveNext(i);}else{logger("error:i="+i.ToString()+", have no move next");}
                    }
                }
            }
            public static FieldInfo m_Seconds=typeof(UnityEngine.WaitForSeconds).GetField("m_Seconds",(BindingFlags)(-1));
            static System.Collections.IEnumerator Postfix(System.Collections.IEnumerator __result){
                while(__result.MoveNext()){
                    if(__result.Current is UnityEngine.WaitForSeconds){
                        yield return new UnityEngine.WaitForSeconds(val*(float)(m_Seconds.GetValue(__result.Current)));
                    } else {
                        yield return __result.Current;
                    }
                }
            }
        }
        [Desc("天赋点只增不减")]
        [HarmonyPatch(typeof(DauSys), "AddTalentPoint")]
        class DauSys_AddTalentPoint : Bbool {
            static bool Prefix(int num)=>num>0;
        }

        [Desc("小鸟节算数无条件正确")]
        [HarmonyPatch(typeof(FeastSys), "CheckQuesAnswer")]
        class FeastSys_CheckQuesAnswer : Bbool {
            static IEnumerable<CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable<CodeInstruction> instructions) {
                logger("%%PLUGIN_ID%%-正在注入类别"+__originalMethod.DeclaringType.Name+"的"+__originalMethod.Name+"方法");
                instructions = new CodeMatcher(instructions)
                    .MatchForward(false, // false = move at the start of the match, true = move at the end of the match
                        new CodeMatch(OpCodes.Ldfld,typeof(FeastSys).GetField("tempQueAns",(BindingFlags)(-1)))
                    ).Repeat( matcher => // Do the following for each match
                        matcher
                        .SetAndAdvance(
                            OpCodes.Ldfld,typeof(FeastSys).GetField("quesIndex",(BindingFlags)(-1))
                        )
                        .Advance(1)
                    ).InstructionEnumeration();
                return instructions;
            }
        }

        [Desc("天赋概率加成最小值",100)]
        [HarmonyPatch(typeof(DauSys), "TalentProb")]
        class DauSys_TalentProb : Bint {
            public static int val;public void Init()=>base.Init(ref val);
            static int Postfix(int res)=>Math.Max(res, val);
        }
        [Desc("烹饪系统双倍概率最小值", 100)]
        [HarmonyPatch(typeof(ItemSys), "DoubleCookingRate")]
        class ItemSys_DoubleCookingRate : Bint {
            public static int val;public void Init()=>base.Init(ref val);
            static int Postfix(int res)=>Math.Max(res, val);
        }
        [Desc("农田系统双倍概率最小值", 100)]
        [HarmonyPatch(typeof(ItemSys), "DoubleFoodRate")]
        class ItemSys_DoubleFoodRate : Bint {
            public static int val;public void Init()=>base.Init(ref val);
            static int Postfix(int res)=>Math.Max(res, val);
        }
        [Desc("烹饪系统星号烹饪最小值", 95)]
        [HarmonyPatch(typeof(ItemSys), "GoodCookingRate")]
        class ItemSys_GoodCookingRate : Bint {
            public static int val;public void Init()=>base.Init(ref val);
            static int Postfix(int res)=>Math.Max(res, val);
        }
        [Desc("农田系统优质作物最小值", 85)]
        [HarmonyPatch(typeof(ItemSys), "RareFoodRate")]
        class ItemSys_RareFoodRate : Bint {
            public static int val;public void Init()=>base.Init(ref val);
            static int Postfix(int res)=>Math.Max(res, val);
        }
        [Desc("偷窃成功率最小值", 100)]
        [HarmonyPatch(typeof(MapSys), "StealSuccessRate")]
        class MapSys_StealSuccessRate : Bint {
            public static int val;public void Init()=>base.Init(ref val);
            static int Postfix(int res)=>Math.Max(res, val);
        }
        [Desc("炼金成功率最小值",100)]
        [HarmonyPatch(typeof(ItemSys), "AlchemySuccessRate")]
        class ItemSys_AlchemySuccessRate : Bint {
            public static int val;public void Init()=>base.Init(ref val);
            static int Postfix(int res)=>Math.Max(res, val);
        }
        [Desc("农田进度乘数",10)]
        [HarmonyPatch(typeof(ItemSys), "AddFarmPrg")]
        class ItemSys_AddFarmPrg : Bint {
            public static int val;public void Init()=>base.Init(ref val);
            static void Prefix(ref int num){
                num*=val;
            }
        }
    }
}

